package com.dingwen.stusprbooes.dao;

import com.alibaba.fastjson.JSON;
import com.dingwen.stusprbooes.constant.ElasticsearchConstant;
import com.dingwen.stusprbooes.entity.GoodsEntity;
import com.dingwen.stusprbooes.util.CrawlUtil;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.stereotype.Repository;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * document dao 基类
 *
 * @author dingwen
 * 2021/6/27 21:32
 **/
@Repository
public class DocumentBaseDao<T> {
    /**
     * 请求
     */
    private final IndexRequest request;

    /**
     * 其他高水平的客户
     */
    private final RestHighLevelClient restHighLevelClient;

    public DocumentBaseDao(RestHighLevelClient restHighLevelClient) {
        this.restHighLevelClient = restHighLevelClient;
        this.request = new IndexRequest(ElasticsearchConstant.INDEX);
        this.request.timeout(TimeValue.timeValueMillis(3000));
    }


    /**
     * 保存
     *
     * @param t t
     * @return {@link IndexResponse}
     * @throws IOException io exception
     */
    public IndexResponse save(T t) throws IOException {
        request.source(JSON.toJSONString(t), XContentType.JSON);
        return restHighLevelClient.index(request, RequestOptions.DEFAULT);
    }


    /**
     * 删除通过id
     *
     * @param id id
     * @return {@link DeleteRequest}
     */
    public DeleteResponse removeById(String id) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(ElasticsearchConstant.INDEX, id);
        return restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
    }

    /**
     * 批量插入
     * TODO 等待完善
     *
     * @return {@link BulkResponse}
     */
    public BulkResponse save() throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("60s");
        List<GoodsEntity> goodsEntityList = CrawlUtil.crawl();
        goodsEntityList.stream().parallel().forEach(goodsEntity -> {
            bulkRequest.add(new IndexRequest(ElasticsearchConstant.INDEX)
                    .id(goodsEntity.getId())
                    .source(JSON.toJSONString(goodsEntity), XContentType.JSON));
        });
        return restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
    }


    /**
     * 根据文档ID查看文档是否存在
     *
     * @param id id
     * @return boolean
     * @throws IOException io exception
     */
    public boolean exists(String id) throws IOException {

        GetRequest getRequest = new GetRequest(ElasticsearchConstant.INDEX, id);
        // 取消返回 _sources 上下文
        getRequest.fetchSourceContext(new FetchSourceContext(false));

        return restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
    }


    /**
     * 根据ID查询文档
     *
     * @param id id
     * @return {@link GetResponse}
     * @throws IOException ioexception
     */
    public GetResponse findById(String id) throws IOException {
        GetRequest getRequest = new GetRequest(ElasticsearchConstant.INDEX, id);
        return restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
    }


    /**
     * 修改
     *
     * @param t  t
     * @param id id
     * @return {@link UpdateResponse}
     * @throws IOException io exception
     */
    public UpdateResponse modify(T t, String id) throws IOException {
        UpdateRequest updateRequest = new UpdateRequest(ElasticsearchConstant.INDEX, id);
        updateRequest.doc(JSON.toJSONString(t), XContentType.JSON);

        return restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
    }


    /**
     * 搜索页面突出
     *
     * @param keywords 关键字
     * @param pageNo   页面没有
     * @param pageSize 页面大小
     * @return {@link List<Map<String, Object>>}
     * @throws IOException io exception
     */
    public List<Map<String, Object>> searchPageForHighlight(String keywords, int pageNo, int pageSize) throws IOException {
        if (pageNo <= 1) {
            pageNo = 1;
        }
        // 条件搜索
        SearchRequest searchRequest = new SearchRequest(ElasticsearchConstant.INDEX);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 分页
        sourceBuilder.from(pageNo);
        sourceBuilder.size(pageSize);
        // 精准匹配
        MatchPhraseQueryBuilder termQueryBuilder = QueryBuilders.matchPhraseQuery("name", keywords);
        sourceBuilder.query(termQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name");
        highlightBuilder.requireFieldMatch(false);
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        sourceBuilder.highlighter(highlightBuilder);

        // 执行搜索
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 解析结果
        List<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit documentField : searchResponse.getHits().getHits()) {
            Map<String, HighlightField> highlightFields = documentField.getHighlightFields();
            HighlightField name = highlightFields.get("name");
            Map<String, Object> sourceAsMap = documentField.getSourceAsMap();
            if (name != null) {
                Text[] fragments = name.fragments();
                StringBuilder newName = new StringBuilder();
                for (Text text : fragments) {
                    newName.append(text);
                }
                sourceAsMap.put("name", newName.toString());
            }
            list.add(sourceAsMap);
        }
        return list;
    }

    /**
     * 得到实体class
     *
     * @return {@link Class<T>}
     */
    @SuppressWarnings("unchecked")
    public Class<T> getEntityClass() {
        Type superClass = this.getClass().getGenericSuperclass();
        Type[] actualTypeArguments = ((ParameterizedType) superClass).getActualTypeArguments();
        return (Class<T>) actualTypeArguments[0];
    }
}
